//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

/* eslint no-console: ["error", { allow: ["warn", "dir", "log"] }] */

'use strict';

// There is an edge case for losing reports - if someone has an e-mail addresses added to a service
// account, and also is linked themselves, then it's possible that they may get multiple reports or
// may have the reports masked.

const fs = require('fs');
const path = require('path');
const pug = require('pug');
const Q = require('q');
const qlimit = require('qlimit');

const emailRender = require('../../lib/emailRender');

function sendReports(context) {
  const mailProvider = context.operations.providers.mailProvider;
  if (!mailProvider) {
    return Q.reject(new Error('No mailProvider is available to send messages'));
  }

  const reportsByRecipient = context.reportsByRecipient;
  if (!reportsByRecipient) {
    return Q.reject(new Error('No consolidated reports were generated by recipient'));
  }

  const overrideSendWithPath = context.settings.fakeSend;
  if (overrideSendWithPath) {
    console.warn(`Instead of sending mail, mail will be written to ${overrideSendWithPath}`);
    try {
      fs.mkdirSync(overrideSendWithPath);
    } catch (ignored) {
      console.log(`While creating directory to store e-mails instead of sending, received: ${ignored.message}`);
    }
  }

  const limit = qlimit(1);
  const recipients = Array.from(reportsByRecipient.keys());
  const sendIndividualReport = sendReport.bind(null, context, mailProvider, reportsByRecipient);
  return Q.allSettled(recipients.map(limit(sendIndividualReport)))
    .thenResolve(context);
}

function resolveAddress(context, upn) {
  const operations = context.operations;
  const providers = operations.providers;
  if (!providers.mailAddressProvider) {
    return Q.reject(new Error('No mailAddressProvider is available in this application instance'));
  }
  const deferred = Q.defer();
  providers.mailAddressProvider.getAddressFromUpn(upn, (error, address) => {
    return error ? deferred.reject(error) : deferred.resolve(address);
  });
  return deferred.promise;
}

function recipientTypeToAddress(context, address) {
  const i = address.indexOf(':');
  if (i < 0) {
    return Q.reject(new Error('Invalid consolidated address format'));
  }
  const type = address.substr(0, i);
  const remainder = address.substr(i + 1);
  if (type === 'mail') {
    return Q(remainder);
  } else if (type === 'upn') {
    return resolveAddress(context, remainder);
  } else {
    return Q.reject(`Unsupported consolidated address type ${type}`);
  }
}

function consolidatedActionRequired(report) {
  for (let i = 0; i < report.length; i++) {
    const definition = report[i].definition;
    if (definition.isActionRequired) {
      return true;
    }
  }
  return false;
}

function renderReport(context, report, address) {
  const options = {
    github: {
      consolidated: report,
    },
    viewServices: context.operations.providers.viewServices,
    to: address,
  };
  const basedir = path.join(__dirname, 'views');
  const view = path.join(basedir, 'administrator.pug');
  options.pretty = true;
  options.basedir = basedir;
  let html = null;
  try {
    html = pug.renderFile(view, options);
  } catch (renderingProblem) {
    console.warn(renderingProblem);
    throw renderingProblem;
  }
  return html;
}

function sendReport(context, mailProvider, reportsByRecipient, recipientKey) {
  const report = reportsByRecipient.get(recipientKey);
  const overrideSendWithPath = context.settings.fakeSend;
  const fromAddress = context.settings.fromAddress;
  if (!fromAddress && !overrideSendWithPath) {
    return Q.reject(new Error('No from address is configured for reports in the github.jobs.reports.mail.from value'));
  }
  return recipientTypeToAddress(context, recipientKey).then(address => {
    if (!report || !report.length) {
      return Q(context);
    }
    const isActionRequired = consolidatedActionRequired(report);
    const classification = isActionRequired ? 'action' : 'information';
    const html = renderReport(context, report, address);
    const viewOptions = {
      html: html,
    };
    const basedir = path.resolve(__dirname, '../../');
    const deferred = Q.defer();
    emailRender.render(basedir, 'report', viewOptions, (renderError, mailContent) => {
      if (renderError) {
        return deferred.reject(renderError);
      }
      // Store the e-mail instead of sending
      if (overrideSendWithPath) {
        const filename = path.join(overrideSendWithPath, `${address}.html`);
        return fs.writeFile(filename, mailContent, 'utf8', error => {
          if (error) {
            console.warn(`Trouble writing ${filename} ${error}`);
          } else {
            console.log(`Wrote ${filename}`);
          }
          return error ? deferred.reject(error) : deferred.resolve();
        });
      }

      // Send the e-mail
      const actionSubject = isActionRequired ? 'Action required: ' : '';
      const mailOptions = {
        to: address,
        from: fromAddress,
        subject: `${actionSubject}GitHub digest for ${address}`,
        classification: classification,
        headline: isActionRequired ? 'Your GitHub updates' : 'GitHub updates',
        service: 'Microsoft GitHub',
        reason: 'This digest report is provided to all managed GitHub organization owners, repository admins, and team maintainers. This report was personalized and sent directly to ' + address,
        content: mailContent,
      };
      mailProvider.sendMail(mailOptions, (mailError , mailResult) => {
        const customData = {
          receipt: mailResult,
        };
        customData.eventName = mailError ? 'JobReportSendFailed' : 'JobReportSendSuccess';
        if (mailError) {
          context.insights.trackException(mailError, customData);
        } else {
          context.insights.trackEvent('JobMailProviderReportSent', customData);
        }
        return mailError ? deferred.reject(mailError) : deferred.resolve();
      });
    });
    return deferred.promise;
  });
}

module.exports = sendReports;
